using System;
using threading = System.Threading;
using Gadgeteer.Interfaces.Timed;

namespace Gadgeteer.Interfaces.Fakes
{
    public class ThreadingTimerFactory : TimerFactoryBase, ITimerFactory
    {
        public override ITimer Create(TimeSpan dueIn, TimeSpan interval, TimerBehavior behavior)
        {
            return new TimerWrapper(
                dueIn,
                interval,
                behavior);
        }

        private class TimerWrapper : ITimer, IDisposable
        {
            private threading.Timer timer;

            public TimerWrapper(TimeSpan dueIn, TimeSpan interval, TimerBehavior behavior)
            {
                Behavior = behavior;
                Interval = interval;
                DueIn = dueIn;
            }

            private void TickCallBack(object state)
            {
                switch (Behavior)
                {
                    case TimerBehavior.RunContinuously:
                        OnTick();
                        return;
                    case TimerBehavior.RunOnce:
                    default:
                        Stop();
                        OnTick();
                        return;
                }
            }

            public TimerBehavior Behavior { get; private set; }

            private TimeSpan _dueIn;
            public TimeSpan DueIn
            {
                get { return _dueIn; }
                set
                {
                    if (_dueIn != value)
                    {
                        _dueIn = value;
                        AdjustTimer();
                    }
                }
            }

            private TimeSpan _interval;
            public TimeSpan Interval
            {
                get { return _interval; }
                set
                {
                    if (_interval != value)
                    {
                        _interval = value;
                        AdjustTimer();
                    }
                }
            }

            private void AdjustTimer()
            {
                if (timer != null)
                {
                    timer.Change(DueIn, Interval);
                }
            }

            public bool IsRunning
            {
                get { return timer != null; }
            }

            public void Restart()
            {
                AdjustTimer();
            }

            public void Start()
            {
                if (timer != null)
                    throw new InvalidOperationException("Timer already started");

                timer = new threading.Timer(
                    new threading.TimerCallback(TickCallBack),
                    null, DueIn, Interval);
            }

            public void Stop()
            {
                if (timer != null)
                {
                    var timerRef = timer;
                    timer = null;
                    timerRef.Dispose();
                }
            }

            private void OnTick()
            {
                var ev = Tick;
                if (ev != null)
                    ev(this);
            }

            public event TickEventHandler Tick;

            #region Dispose

            private bool disposed = false;

            public void Dispose(bool disposing)
            {
                if (disposed == false)
                {
                    if (disposing)
                    {
                        if (timer != null)
                        {
                            timer.Dispose();
                        }
                    }
                }
            }

            public void Dispose()
            {
                Dispose(true);
                GC.SuppressFinalize(this);
            }
            #endregion
        }
    }
}
